
/**
 * 地面类
 * todo: 增加地板类别和参数等
 */

import A3StaticObject from "./A3StaticObject.js";
import A3ObjectTypes from "../../A3ObjectTypes.js";
import { CubeSideIndex, isNumber } from "../../common/Common.js";
import { Quaternion } from "../../3rdParty/three.js/build/three.module.js";
import { Axis } from "../../graphics/Graphics.js";
import A3ObjectGroup from "../../A3ObjectGroup.js";

/**
 * 大地类
 */

class A3Ground extends A3StaticObject {
  
  type=A3ObjectTypes.Ground;
  name=A3ObjectTypes.Ground;

  defaultTexture = {
    All: {
      url:"../../../textures/grassland.jpg",
      size: [10, 10],
    }
  };
  
  constructor( width, height, depth, autoUpdateTexture=true ) {

    width = isNaN( width ) ? 10: width;
    height = isNaN( height ) ? 0.01: height;
    depth = isNaN( depth ) ? 10: depth;

    super( width, height, depth );

    this.setColor( .7, .7, .7 );
    this.position.set( 0, -height/2, 0 );
    this.threeObject.position.set( 0, -height/2, 0 );

    if( autoUpdateTexture ) this.updateTexture();

  }

  updateTexture( texture ) {

    if( !texture ){
      texture = this.defaultTexture;
    }

    for( var side in texture ) {
      
      if( !(side in CubeSideIndex) ) continue;

      const url = texture[side].url;
      // const size = isNumber( texture[side].size ) ? texture[side].size : 1;
      let config = {};

      if( texture[side].size ){

        const u = texture[side].size[0];
        const v = texture[side].size[1];

        switch( side ){

          case "Top":
          case "Bottom":
            config = {
              repeat:[ u >= 0 ? this.width / u : 1, v >= 0 ? this.depth / v : 1 ],
            }
            break;
          case "Left":
          case "Right":
            config = {
              repeat:[ u >= 0 ? this.width / u : 1, v >= 0 ? this.height / v : 1 ],
            }
            break;
          case "Front":
          case "Back":
            config = {
              repeat:[ u >= 0 ? this.depth / u : 1, v >= 0 ? this.height / v : 1 ],
            }
            break;
          default:
            // config = {
            //   repeat:[ 1, 1 ],
            // }
            break;
        }

      }

      if( texture[side].offset ){
        config["offset"]= texture[side].offset;
      }
      
      if( texture[side].rotation ){
        config["rotation"]= texture[side].rotation;
      }

      if( texture[side].center ){
        config["center"]= texture[side].center;
      }

      this.setTexture( url, CubeSideIndex[side], config );
      
    }

  }

}

/**
 * 天花板类
 */
class A3Ceiling extends A3Ground {

  type=A3ObjectTypes.Ceiling;
  name=A3ObjectTypes.Ceiling;

  defaultTexture = {
    Bottom: {
      url:"../../../textures/ceiling_1.jpg",
      size:[ 100, 100 ],
    }
  };

  constructor( width, height, depth, autoUpdateTexture=true ) {
    
    super( width, height, depth, false );

    // this.setColor( .10, .5, .86 );
    if( autoUpdateTexture) this.updateTexture();

  }

}

/**
 * 地板类
 */
class A3Floor extends A3Ground {

  type=A3ObjectTypes.Floor;
  name=A3ObjectTypes.Floor;

  defaultTexture = {
    Top: {
      url:"../../../textures/floor.jpg",
      size: [ 100, 100 ],
    }
  };

  constructor( width, height, depth, autoUpdateTexture=true ) {
    
    super( width, height, depth, false );

    // this.setColor( .10, .5, .86 );
    if( autoUpdateTexture) this.updateTexture();

  }
}

/**
 * 墙体类
 */
class A3Wall extends A3Ground {

  type=A3ObjectTypes.Wall;
  name=A3ObjectTypes.Wall;

  defaultTexture = {
    Left: {
      url:"../../../textures/wall_2.png",
      size: [ 180, 180 ],
    },
    Right: {
      url:"../../../textures/wall_1.png",
      size: [ 180, 180 ],
    }
  };

  constructor( width, height, depth, autoUpdateTexture=true ) {
    
    super( width, height, depth, false );

    if( autoUpdateTexture) this.updateTexture();

  }

}

/**
 * 门类
 */
class A3Door extends A3Ground {
  
  type=A3ObjectTypes.Door;
  name=A3ObjectTypes.Door;

  isOpenable = true;

  defaultTexture = {
    Front: {
      url:"../../../textures/door.jpg",
      size: [ -1, -1 ],
      offset:[0, 0],
      center:[0.5, 0.5],
      rotation:0
    },
    Back: {
      url:"../../../textures/door.jpg",
      size: [ -1, -1 ],
      offset:[0, 0],
      center:[0.5, 0.5],
      rotation:Math.PI
    }
  }

  isLeft = true;
  isOutward = true;
  
  jointAngle = 0.0;
  jointRange = [ 0, Math.PI / 2 ];

  isFixed = false;

  constructor( width, height, depth, isLeft=false, isOutward=true, autoUpdateTexture=true ) {

    width = isNumber( width ) ? width : 10;
    height = isNumber( height ) ? height : 300;
    depth = isNumber( depth ) ? depth : 100;
    
    super( width, height, depth, false );

    this.defaultTexture.Front.rotation = isLeft ? 0 : Math.PI;
    this.defaultTexture.Back.rotation = isLeft ? Math.PI : 0;

    if( autoUpdateTexture) this.updateTexture();

    // 设置开门方式
    this.hasUserTransform = true;
    this.setOpenningMode( isLeft, isOutward );

  }

  initActions(){

    this.addAction( "open", "KeyO", this.open.bind(this) );
    this.addAction( "close", "KeyC", this.close.bind(this) );
    this.addAction( "set_angle", "KeyT", this.setAngle.bind(this) );

  }

  setOpenningMode( isLeft=true, isOutward=true ) {
    
    this.isLeft = isLeft;
    this.isOutward = isOutward;
    

    if( isLeft ) {
      this.setUserPosition( 0, 0, -this.depth / 2 );
    }
    else {
      this.setUserPosition( 0, 0, this.depth / 2 );
    }

  }

  jointMove( angle ) {

    if( this.isFixed ) return;
    
    const quaternion = this.quaternion;
    const position = this.position;

    const direction = (this.isLeft === this.isOutward) ? -1 : 1;

    quaternion.multiply( new Quaternion().setFromAxisAngle( Axis.Y, angle * direction ));
    
    this.updateWorldTransform( position, quaternion );

  }

  setAngle( args ) {
    // console.log( `current angle:${this.jointAngle}, target angle:${args}`);

    var angle = this.jointAngle;

    if( isNumber(args) ){
      angle = args;
    }
    else if( args && args instanceof Array ) {
      if( args.length > 0 ){
        angle = args[0];
      }
    }


    if( angle >= this.jointRange[0]  && angle <= this.jointRange[1] ) {
      const diff = angle - this.jointAngle;
      this.jointMove( diff );
      this.jointAngle = angle;
    }

  }

  store(){
    super.store();

    this.lastestStatus["jointAngle"] = this.jointAngle;
  }

  reset(){
    super.reset();

    this.jointAngle = this.lastestStatus["jointAngle"];
  }

  open() {

    this.setAngle( this.jointRange[1] );

  }

  close() {

    this.setAngle( this.jointRange[0] );

  }

  toJson() {

    const data = super.toJson();

    data["isLeft"] = this.isLeft;
    data["isOutward"] = this.isOutward;

    return data;

  }

  static fromJson( data, obj ) {

    if ( obj === null || obj === undefined ) {
      obj = new A3Door( data.width, data.height, data.depth, data.isLeft, data.isOutward );
    }
    
    super.fromJson( data, obj );
    
    return obj;

  }

}

/**
 * 双门
 */
class A3DoubleDoor extends A3ObjectGroup {

  type=A3ObjectTypes.DoubleDoor;
  name=A3ObjectTypes.DoubleDoor;

  isOpenable = true;

  leftJointRange = [ 0, Math.PI ];
  rightJointRange = [ 0, Math.PI ];

  leftJointAngle = 0.0;
  rightJointAngle = 0.0;

  constructor( width, height, depth, isOutward, autoUpdateTexture=true ) {

    width = isNumber( width ) ? width : 10;
    height = isNumber( height ) ? height : 300;
    depth = isNumber( depth ) ? depth : 200;

    super( width, height, depth );

    const left = new A3Door( width, height, depth/2, true, isOutward, autoUpdateTexture );
    left.setPosition( 0, 0, depth / 2 );
    const right = new A3Door( width, height, depth/2, false, isOutward, autoUpdateTexture );
    right.setPosition( 0, 0, -depth / 2 );

    this.addChild( left );
    this.addChild( right );

    this.left = left;
    this.right = right;

    // this.setPosition( 0, height/2, 0 );

  }

  setDoor( door, isRight ) {
    if( isRight ) {
      this.right = door;
    }
    else {
      this.left = door;
    }
  }

  initActions(){

    this.addAction( "open", "KeyO", this.open.bind(this) );
    this.addAction( "close", "KeyC", this.close.bind(this) );
    this.addAction( "set_angle", "KeyT", this.setAngle.bind(this) );

  }

  setAngle( args ) {

    console.log("set:", args );
    var leftAngle = this.leftJointAngle;
    var rightAngle = this.rightJointAngle;

    if( isNumber(args) ){
      leftAngle = args;
      rightAngle = args;
    }
    else if( args && args instanceof Array ) {
      if( args.length === 1 ){
        leftAngle = args[0];
        rightAngle = args[0];
      }
      else if( args.length >= 2 ) {
        leftAngle = args[0];
        rightAngle = args[1];
      }
    }

    this.left.setAngle( leftAngle );
    this.right.setAngle( rightAngle );

  }


  open(args) {

    if( args.length === 0 ) {
      this.left.open();
      this.right.open();
    }
    
    if( args.length >= 1 && args[0] ){
      this.left.open();
    }

    if( args.length >= 2 && args[1] ){
      this.right.open();
    }

  }

  close(args) {

    if( args.length === 0 ) {
      this.left.close();
      this.right.close();
    }

    if( args.length >= 1 && args[0] ){
      this.left.close();
    }

    if( args.length >= 2 && args[1] ){
      this.right.close();
    }


  }

  toJson() {

    const data = super.toJson();

    data["isOutward"] = this.isOutward;
    // todo: 保存门参数
    return data;

  }

  static fromJson( data, obj, passChild=false ) {

    if ( obj === null || obj === undefined ) {
      obj = new A3DoubleDoor( data.width, data.height, data.depth, data.isOutward );
    }

    // 取消自动加载子演员
    super.fromJson( data, obj, true );

    //todo: 加载门参数

    return obj;

  }

}

/**
 * 组合窗户
 */
class A3CombinedWindow extends A3StaticObject {

  type=A3ObjectTypes.CombinedWindow;
  name=A3ObjectTypes.CombinedWindow;

  isOpenable = true;

  materialMap = new Map();

  constructor() {
    super( 10, 10, 10 );

  }

}

export default A3Ground;

export {

  A3Ground,
  A3Ceiling,
  A3Floor,
  A3Wall,
  A3Door,
  A3DoubleDoor,
  A3CombinedWindow,

}